package cn.itsource.ymcc.service.impl;

import cn.itsource.ymcc.constant.KillConstants;
import cn.itsource.ymcc.domain.CourseDoc;
import cn.itsource.ymcc.domain.KillCourse;
import cn.itsource.ymcc.dto.PreOrderDto;
import cn.itsource.ymcc.mapper.KillCourseMapper;
import cn.itsource.ymcc.openfeign.EsCourseOpenFeign;
import cn.itsource.ymcc.result.JsonResult;
import cn.itsource.ymcc.service.IKillCourseService;
import cn.itsource.ymcc.util.AssertUtil;
import cn.itsource.ymcc.util.CodeGenerateUtils;
import cn.itsource.ymcc.vo.UserKillVo;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author zongchangjiang
 * @since 2023-08-09
 */
@Service
public class KillCourseServiceImpl extends ServiceImpl<KillCourseMapper, KillCourse> implements IKillCourseService {
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private RedisTemplate<String,Object> redisTemplate;
    @Autowired
    private EsCourseOpenFeign esCourseOpenFeign;
    @Autowired
    private RocketMQTemplate rocketMQTemplate;
    //实现用户秒杀逻辑
    @Override
    public JsonResult userKill(UserKillVo userKillVo) {
        //1. 秒杀判断：是否登录，秒杀码是否合法，秒杀时间是否合法，用户是否已经秒杀过，秒杀数量限制
        //2.秒杀码是否合法
        //使用jsr303的校验来实现


        //用户是否已经秒杀过当前课程
        AssertUtil.isTrue(!redisTemplate.hasKey(KillConstants.KILL_RECORD + "_" + userKillVo.getLoginId()
                + "_" + userKillVo.getKillCourseId()), "请勿重复秒杀同一课程！");
        //秒杀时间是否合法
        //获取系统当前时间
        KillCourse killCourse = baseMapper.selectOne(new QueryWrapper<KillCourse>().eq("course_id",userKillVo.getKillCourseId()));

        //判断用户购买的数量
        AssertUtil.isFalse(userKillVo.getKillCount()>killCourse.getKillLimit(),"您购买的数量超出上限了");



        Date date = new Date();
        AssertUtil.isTrue(date.after(killCourse.getStartTime()) && date.before(killCourse.getEndTime()), "秒杀活动时间不合法！");

        //2. Redisson分布式锁：信号量减库存  库存预热的key
        RSemaphore semaphore = redissonClient.getSemaphore(KillConstants.KILL_STORE + userKillVo.getKillCourseId());
        //分布式锁：尝试抢锁（尝试扣减库存，减本次秒杀的数量）
        boolean b = semaphore.tryAcquire(userKillVo.getKillCount());
        String orderNo = null;
        if(b){//只有抢成功了 才是true
            //伪下单
            orderNo = CodeGenerateUtils.generateOrderSn(userKillVo.getLoginId());
            PreOrderDto preOrderDto = new PreOrderDto();
            preOrderDto.setOrderNo(orderNo);
            preOrderDto.setUserId(userKillVo.getLoginId());
            preOrderDto.setTitle("秒杀订单");
            preOrderDto.setOrderType(1);
            preOrderDto.setPrice(killCourse.getKillPrice());
            preOrderDto.setCount(userKillVo.getKillCount());
            preOrderDto.setCourseId(killCourse.getCourseId());
            preOrderDto.setCourseName(killCourse.getCourseName());
            preOrderDto.setCoursePic(killCourse.getCoursePic());
            preOrderDto.setKillCourseId(killCourse.getId());
            //把伪订单保存redis里面  方便 后面下订单使用
            redisTemplate.opsForValue().set(KillConstants.KEY_PREFIX_PRE_ORDER+orderNo,preOrderDto);
            //5. 发送延迟消息到MQ，用作下单超时处理（5分钟）  明天做  todo
            rocketMQTemplate.syncSend(KillConstants.TOPIC_KILL_COURSE_DELAY_MESSAGE+":"+KillConstants.TAGS_KILL_COURSE_DELAY_MESSAGE,
                    MessageBuilder.withPayload(orderNo).build(),2000,5);
            //6. 保存用户秒杀记录到Redis防止重复秒杀  KILL_RECORD_用户id_秒杀课程id =》 value随意
            redisTemplate.opsForValue().set(KillConstants.KILL_RECORD+"_"+userKillVo.getLoginId()+"_"+killCourse.getId(), 1);
        }
        //7. 返回订单号给前端，用作下单依据
        if(orderNo == null){
            return JsonResult.success().setCode(5001).setMessage("抢购失败，请下次努力");
        }
        return JsonResult.success().setData(orderNo);
    }

    @Override
    public JsonResult oneByOrder(String orderNo) {
        PreOrderDto preOrderDto = (PreOrderDto)redisTemplate.opsForValue().get(KillConstants.KEY_PREFIX_PRE_ORDER + orderNo);
        JsonResult jsonResult = esCourseOpenFeign.queryOne(preOrderDto.getCourseId());
        Map<String ,Object> result = new HashMap<>();
        result.put("courseInfos", JSON.parseObject(jsonResult.getData().toString(), CourseDoc.class));
        result.put("totalAmount",preOrderDto.getPrice());
        return JsonResult.success().setData(result);
    }
}
